package com.larly.service.impl;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.larly.common.DelectRequest;
import com.larly.common.IdRequest;
import com.larly.exception.BusinessException;
import com.larly.exception.ErrorCode;
import com.larly.exception.ThrowUtils;
import com.larly.manager.upload.UploadFile;
import com.larly.mapper.NoteVersionsMapper;
import com.larly.model.domain.NoteLike;
import com.larly.model.domain.NoteVersions;
import com.larly.model.domain.Notes;
import com.larly.model.domain.User;
import com.larly.model.dto.notes.AddNotesRequest;
import com.larly.model.dto.notes.EditNotesRequest;
import com.larly.model.dto.notes.RollbackNotesRequest;
import com.larly.model.dto.notes.SelectNotesRequest;
import com.larly.model.enums.notes.NotesIsDeletedEnum;
import com.larly.model.enums.notes.NotesIsPinne;
import com.larly.model.enums.notes.NotesStatusEnum;
import com.larly.model.vo.NoteVersionsVO;
import com.larly.model.vo.NotesVO;
import com.larly.model.vo.UserVO;
import com.larly.service.NoteLikeService;
import com.larly.service.NotesService;
import com.larly.mapper.NotesMapper;
import com.larly.service.NotificationService;
import com.larly.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 笔记服务实现类
 * 处理笔记的CRUD、版本管理（仅内容变更时记录）、置顶、回收站等核心业务逻辑
 * 所有ID类型统一使用Integer
 */
@Service
@Slf4j
public class NotesServiceImpl extends ServiceImpl<NotesMapper, Notes>
        implements NotesService {

    @Resource
    private NoteVersionsMapper noteVersionsMapper;

    @Resource
    @Lazy
    private UserService userService;

    @Resource
    private NoteLikeService noteLikeService;

    @Resource
    private UploadFile uploadFile;

    @Resource
    private NotificationService notificationService;


    /**
     * 添加新笔记
     *
     * @param addNotesRequest 新增笔记请求DTO
     * @return 新增笔记的ID（Integer类型）
     */
    @Override
    public long addNote(AddNotesRequest addNotesRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(ObjUtil.isNull(addNotesRequest), ErrorCode.PARAM_ERROR);

        // 校验用户登录状态
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN, "用户未登录");
        Integer userId = loginUser.getId();
       ThrowUtils.throwIf(userId == null, ErrorCode.PARAM_ERROR, "用户id不能为空");

        // 校验核心参数
        String title = addNotesRequest.getTitle();
        String content = addNotesRequest.getContent();
        ThrowUtils.throwIf(ObjUtil.isEmpty(title), ErrorCode.PARAM_ERROR, "标题不能为空");
        ThrowUtils.throwIf(title.length() > 128, ErrorCode.PARAM_ERROR, "标题过长");
        ThrowUtils.throwIf(ObjUtil.isEmpty(content), ErrorCode.PARAM_ERROR, "内容不能为空");
        // 校验笔记的草稿/公开状态
        String status = addNotesRequest.getStatus();
        if (status != null){
            NotesStatusEnum statusEnum = NotesStatusEnum.getByKey(status);
            ThrowUtils.throwIf(statusEnum == null, ErrorCode.PARAM_ERROR, "笔记状态错误");
        }

        // DTO转换为实体
        Notes notes = new Notes();
        BeanUtils.copyProperties(addNotesRequest, notes);
        notes.setUserId(userId);

        // 保存并返回ID
        boolean save = this.save(notes);
        ThrowUtils.throwIf(!save, ErrorCode.OPERATION_DENIED, "添加笔记失败");

        log.info("新增笔记成功，ID: {}", notes.getId());
        return notes.getId();
    }

    /**
     * 编辑笔记（仅标题/内容变更时记录版本）
     *
     * @param editNotesRequest 编辑笔记请求DTO
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean editNote(EditNotesRequest editNotesRequest,HttpServletRequest request) {
        // 1. 校验参数
        ThrowUtils.throwIf(ObjUtil.isNull(editNotesRequest), ErrorCode.PARAM_ERROR);
        Integer noteId = editNotesRequest.getId();
        ThrowUtils.throwIf(ObjUtil.isNull(noteId), ErrorCode.PARAM_ERROR, "笔记ID不能为空");

        // 2. 查询原笔记
        Notes oldNotes = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(oldNotes), ErrorCode.PARAM_ERROR, "笔记不存在");

        // 校验笔记是不是本人创建的
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        ThrowUtils.throwIf(!userId.equals(oldNotes.getUserId()), ErrorCode.NO_AUTH, "无权限");

        // 校验笔记的草稿/公开状态
        String status = oldNotes.getStatus();
        NotesStatusEnum statusEnum = NotesStatusEnum.getByKey(status);
        ThrowUtils.throwIf(statusEnum == null, ErrorCode.PARAM_ERROR, "笔记状态错误");

        // 3. 处理置顶逻辑
        handlePinLogic(editNotesRequest, oldNotes);

        // 4. 检测标题或内容是否变更
        boolean isContentChanged = isTitleOrContentChanged(editNotesRequest, oldNotes);
        if (!isContentChanged) {
            log.info("笔记ID: {} 未修改标题或内容，无需记录版本", noteId);
            // 仅更新非标题/内容字段
            Notes updateNote = new Notes();
            BeanUtils.copyProperties(editNotesRequest, updateNote);
            return this.updateById(updateNote);
        }

        // 5. 内容变更：记录版本并更新
        log.info("笔记ID: {} 标题或内容已变更，开始记录版本", noteId);

        // 5.1 检查版本数量（最多保留3个）
        checkNoteRecord(noteId);

        // 5.2 保存旧版本
        NoteVersions noteVersions = new NoteVersions();
        noteVersions.setNoteId(oldNotes.getId());
        noteVersions.setTitle(oldNotes.getTitle());
        noteVersions.setContent(oldNotes.getContent());
        noteVersions.setUserId(userId);
        int insertResult = noteVersionsMapper.insert(noteVersions);
        ThrowUtils.throwIf(insertResult <= 0, ErrorCode.OPERATION_DENIED, "保存旧版本失败");

        // 5.3 更新笔记内容
        Notes updateNote = new Notes();
        BeanUtils.copyProperties(editNotesRequest, updateNote);
        boolean updateResult = this.updateById(updateNote);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_DENIED, "编辑笔记失败");

        log.info("笔记ID: {} 编辑成功，已记录版本ID: {}", noteId, noteVersions.getId());
        return true;
    }


    /**
     * 回滚笔记至指定版本
     *
     * @param rollbackNotesRequest 回滚请求DTO
     * @return 回滚后的笔记VO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public NotesVO rollbackNote(RollbackNotesRequest rollbackNotesRequest, HttpServletRequest request) {
        // 1. 参数校验
        ThrowUtils.throwIf(ObjUtil.isNull(rollbackNotesRequest), ErrorCode.PARAM_ERROR);
        Integer noteId = rollbackNotesRequest.getNoteId();
        Integer versionId = rollbackNotesRequest.getVersionId();
        ThrowUtils.throwIf(ObjUtil.isNull(noteId) || ObjUtil.isNull(versionId),
                ErrorCode.PARAM_ERROR, "笔记ID和版本ID不能为空");

        // 2. 校验版本和笔记存在性
        NoteVersions targetVersion = noteVersionsMapper.selectById(versionId);
        ThrowUtils.throwIf(ObjUtil.isNull(targetVersion), ErrorCode.PARAM_ERROR, "目标版本不存在");
        Notes currentNote = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(currentNote), ErrorCode.PARAM_ERROR, "笔记不存在");

        User loginUser = userService.getLoginUser(request);

        // 校验是不是本人
        if(!loginUser.getId().equals(targetVersion.getUserId())){
            throw new BusinessException(ErrorCode.NO_PERMISSION, "无操作权限");
        }

        // 3. 检测是否需要回滚（内容是否一致）
        boolean isRollbackNeeded = !ObjUtil.equal(targetVersion.getTitle(), currentNote.getTitle())
                || !ObjUtil.equal(targetVersion.getContent(), currentNote.getContent());
        if (!isRollbackNeeded) {
            log.info("笔记ID: {} 当前内容与目标版本一致，无需回滚", noteId);
            return buildNotesVo(currentNote);
        }

        // 4. 回滚前保存当前版本
        NoteVersions rollbackBeforeVersion = new NoteVersions();
        rollbackBeforeVersion.setNoteId(currentNote.getId());
        rollbackBeforeVersion.setTitle(currentNote.getTitle());
        rollbackBeforeVersion.setContent(currentNote.getContent());
        rollbackBeforeVersion.setUserId(loginUser.getId());
        noteVersionsMapper.insert(rollbackBeforeVersion);
        log.info("笔记ID: {} 回滚前状态已保存为版本ID: {}", noteId, rollbackBeforeVersion.getId());

        // 5. 执行回滚（仅更新标题和内容）
        currentNote.setTitle(targetVersion.getTitle());
        currentNote.setContent(targetVersion.getContent());
        boolean updateResult = this.updateById(currentNote);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_DENIED, "回滚笔记失败");

        log.info("笔记ID: {} 已回滚至版本ID: {}", noteId, versionId);
        return buildNotesVo(currentNote);
    }

    /**
     * 分页查询
     *
     * @param selectNotesRequest
     * @return
     */
    @Override
    public Page<NotesVO> selectNotes(SelectNotesRequest selectNotesRequest) {
        // 构造查询条件
        QueryWrapper<Notes> notesQueryWrapper = this.buildQueryWrapper(selectNotesRequest);

        // 获取分页参数
        Integer pageNum = selectNotesRequest.getPageNum();
        Integer pageSize = selectNotesRequest.getPageSize();
        Page<Notes> page = this.page(new Page<Notes>(pageNum, pageSize), notesQueryWrapper);
        Page<NotesVO> result = this.toDto(page);
        return result;
    }

    /**
     * 点赞笔记（优化版）
     * 解决幂等性问题：同一用户对同一笔记仅能点赞一次
     * 解决并发问题：通过事务和唯一索引保证数据一致性
     *
     * @param noteId  笔记ID
     * @param request 请求上下文（用于获取当前登录用户）
     * @return 是否点赞成功（首次点赞返回true，重复点赞返回false）
     */
    @Override
    @Transactional // 事务保证：点赞记录和点赞数同时成功/失败
    public boolean likeNote(Integer noteId, HttpServletRequest request) {
        // 1. 获取当前登录用户ID（从登录上下文获取，防止伪造）
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        log.info("用户[{}]开始点赞笔记[{}]", userId, noteId);

        // 2. 参数校验
        ThrowUtils.throwIf(ObjUtil.isEmpty(noteId) || ObjUtil.isEmpty(userId),
                ErrorCode.PARAM_ERROR, "笔记ID和用户ID不能为空");
        log.debug("点赞参数校验通过，用户[{}]，笔记[{}]", userId, noteId);

        // 3. 检查笔记是否存在
        Notes note = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(note), ErrorCode.PARAM_ERROR, "笔记不存在");
        log.debug("查询到笔记信息，当前点赞数：{}，笔记[{}]", note.getLikes(), noteId);

        // 4. 检查是否已点赞（防重复，减少无效操作）
        QueryWrapper<NoteLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).eq("note_id", noteId);
        NoteLike existingLike = noteLikeService.getOne(queryWrapper);
        if (ObjUtil.isNotNull(existingLike)) {
            log.warn("用户[{}]已点赞笔记[{}]，无需重复操作", userId, noteId);
            return false; // 重复点赞视为“成功但未生效”
        }

        try {
            // 5. 新增点赞记录（数据库唯一索引保证幂等性）
            NoteLike noteLike = new NoteLike();
            noteLike.setUserId(userId);
            noteLike.setNoteId(noteId);
            boolean save = noteLikeService.save(noteLike);
            ThrowUtils.throwIf(!save , ErrorCode.SYSTEM_ERROR, "点赞记录保存失败");
            log.debug("用户[{}]点赞记录保存成功，笔记[{}]", userId, noteId);

            // 6. 更新笔记点赞数（仅更新点赞字段，性能优化）
            int newLikes = note.getLikes() + 1;
            Notes updateNote = new Notes();
            updateNote.setId(noteId);
            updateNote.setLikes(newLikes);
            boolean updateResult = this.updateById(updateNote);
            ThrowUtils.throwIf(!updateResult, ErrorCode.SYSTEM_ERROR, "点赞数更新失败");
            log.debug("用户[{}]点赞数更新成功，新点赞数：{}，笔记[{}]", userId, newLikes, noteId);

            log.info("用户[{}]点赞笔记[{}]成功，最终点赞数：{}", userId, noteId, newLikes);

            // 添加笔记点赞通知
            if(note != null) {
                notificationService.triggerNoteLikeNotify(noteId, note.getTitle(), userId, note.getUserId());
                log.info("用户[{}]点赞笔记[{}]成功，添加点赞通知成功", userId, noteId);
            }
            return true;

        } catch (Exception e) {
            // 7. 捕获唯一索引冲突（并发场景下的重复请求）
            if (e instanceof SQLIntegrityConstraintViolationException || e.getMessage().contains("uk_user_note")) {
                // uk_user_note是联合唯一索引名
                log.warn("用户[{}]并发点赞笔记[{}]，唯一索引拦截重复操作", userId, noteId);
                return false;
            }
            // 其他异常正常抛出
            log.error("用户[{}]点赞笔记[{}]失败", userId, noteId, e);
            throw new BusinessException(ErrorCode.FAIL);
        }
    }

    /**
     * 取消点赞笔记
     * 解决幂等性问题：同一用户对同一笔记仅能取消点赞一次
     * 解决并发问题：通过事务保证点赞记录和点赞数的一致性
     *
     * @param noteId  笔记ID
     * @param request 请求上下文（用于获取当前登录用户）
     * @return 是否取消点赞成功（首次取消返回true，重复取消返回false）
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 事务保证：删除记录和减少点赞数原子性
    public boolean cancelLikeNote(Integer noteId, HttpServletRequest request) {
        // 1. 获取当前登录用户ID（从上下文获取，防止伪造）
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        log.info("用户[{}]开始取消点赞笔记[{}]", userId, noteId);

        // 2. 参数校验
        ThrowUtils.throwIf(ObjUtil.isEmpty(noteId) || ObjUtil.isEmpty(userId),
                ErrorCode.PARAM_ERROR, "笔记ID和用户ID不能为空");
        log.debug("取消点赞参数校验通过，用户[{}]，笔记[{}]", userId, noteId);

        // 3. 检查笔记是否存在
        Notes note = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(note), ErrorCode.NOTE_NOT_FOUND, "笔记不存在");
        log.debug("查询到笔记信息，当前点赞数：{}，笔记[{}]", note.getLikes(), noteId);

        // 4. 检查是否已点赞（未点赞则无需操作）
        QueryWrapper<NoteLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).eq("note_id", noteId);
        NoteLike existingLike = noteLikeService.getOne(queryWrapper);
        if (ObjUtil.isNull(existingLike)) {
            log.warn("用户[{}]未点赞笔记[{}]，无需取消操作", userId, noteId);
            return false; // 未点赞时取消视为“成功但未生效”
        }

        try {
            // 5. 删除点赞记录
            boolean removeResult = noteLikeService.removeById(existingLike.getId());
            ThrowUtils.throwIf(!removeResult, ErrorCode.SYSTEM_ERROR, "取消点赞记录删除失败");
            log.debug("用户[{}]点赞记录删除成功，笔记[{}]，记录ID: {}", userId, noteId, existingLike.getId());

            // 6. 更新笔记点赞数（确保点赞数不小于0）
            int currentLikes = note.getLikes();
            int newLikes = Math.max(currentLikes - 1, 0); // 防止出现负数点赞
            Notes updateNote = new Notes();
            updateNote.setId(noteId);
            updateNote.setLikes(newLikes);
            boolean updateResult = this.updateById(updateNote);
            ThrowUtils.throwIf(!updateResult, ErrorCode.SYSTEM_ERROR, "取消点赞数更新失败");
            log.debug("用户[{}]取消点赞数更新成功，新点赞数：{}，笔记[{}]", userId, newLikes, noteId);

            log.info("用户[{}]取消点赞笔记[{}]成功，最终点赞数：{}", userId, noteId, newLikes);
            return true;

        } catch (Exception e) {
            log.error("用户[{}]取消点赞笔记[{}]失败", userId, noteId, e);
            throw e; // 事务回滚
        }
    }

    /**
     * 上传笔记封面图片
     *
     * @param file       图片文件
     * @return 是否成功
     */
    @Override
    public String uploadCover(MultipartFile file,HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        String key = String.format("notes/cover/%s/%s", loginUser.getId(), file.getOriginalFilename());
        boolean result = uploadFile.uploadFile(file, key);
        ThrowUtils.throwIf(!result, ErrorCode.FAIL, "上传失败");
        String imgUrl = uploadFile.getImgUrl(key);
        return imgUrl;
    }


    /**
     * 将笔记移至回收站（逻辑删除）
     *
     * @param delectRequest 包含笔记ID的删除请求
     * @return 是否成功
     */
    @Override
    public boolean deleteNote(DelectRequest delectRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(ObjUtil.isNull(delectRequest), ErrorCode.PARAM_ERROR);
        Integer noteId = delectRequest.getId();
        ThrowUtils.throwIf(ObjUtil.isNull(noteId), ErrorCode.PARAM_ERROR, "笔记ID不能为空");

        Notes notes = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(notes), ErrorCode.PARAM_ERROR, "笔记不存在");

        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        ThrowUtils.throwIf(!userId.equals(notes.getUserId()), ErrorCode.NO_AUTH, "无权限删除");

        notes.setIsDeleted(NotesIsDeletedEnum.DELETED.getKey());
        boolean updateResult = this.updateById(notes);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_DENIED, "删除笔记失败");

        log.info("笔记ID: {} 已移至回收站", noteId);
        return true;
    }

    /**
     * 清空回收站（物理删除）
     *
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean clearDeletedNotes(HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();

        // 查询所有回收站笔记ID
        Set<Integer> deletedNoteIds = this.lambdaQuery()
                .select(Notes::getId)
                .eq(Notes::getIsDeleted, NotesIsDeletedEnum.DELETED.getKey())
                .eq(Notes::getUserId, userId)
                .list()
                .stream()
                .map(Notes::getId)
                .collect(Collectors.toSet());

        if (deletedNoteIds.isEmpty()) {
            log.info("回收站为空，无需清理");
            return true;
        }

        // 先删除关联的版本记录
        deletedNoteIds.forEach(noteId -> {
            QueryWrapper<NoteVersions> wrapper = new QueryWrapper<>();
            wrapper.eq("note_id", noteId);
            int deleteCount = noteVersionsMapper.delete(wrapper);
            log.info("删除笔记ID: {} 的关联历史版本 {} 条", noteId, deleteCount);
        });

        // 物理删除笔记
        boolean deleteResult = this.removeBatchByIds(deletedNoteIds);
        ThrowUtils.throwIf(!deleteResult, ErrorCode.OPERATION_DENIED, "清空回收站失败");

        log.info("清空回收站成功，共删除笔记 {} 条", deletedNoteIds.size());
        return true;
    }

    @Override
    public boolean recoverNote(IdRequest idRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(ObjUtil.isNull(idRequest), ErrorCode.PARAM_ERROR);
        Integer noteId = idRequest.getId();
        ThrowUtils.throwIf(ObjUtil.isNull(noteId), ErrorCode.PARAM_ERROR, "笔记ID不能为空");
        Notes notes = this.getById(noteId);
        ThrowUtils.throwIf(ObjUtil.isNull(notes), ErrorCode.PARAM_ERROR, "笔记不存在");
        User loginUser = userService.getLoginUser(request);
        Integer userId = loginUser.getId();
        ThrowUtils.throwIf(!userId.equals(notes.getUserId()), ErrorCode.NO_AUTH, "无权限恢复");
        notes.setIsDeleted(NotesIsDeletedEnum.NOT_DELETED.getKey());
        boolean updateResult = this.updateById(notes);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_DENIED, "恢复笔记失败");
        return true;
    }

    /**
     * 获取笔记历史版本(返回的不是全部notes字段，只有关键字段)
     *
     * @param noteId 笔记ID
     * @return 笔记历史版本列表
     */
    @Override
    public List<NoteVersionsVO> getNoteVersions(Integer noteId) {
        ThrowUtils.throwIf(ObjUtil.isNull(noteId), ErrorCode.PARAM_ERROR, "笔记ID不能为空");
        log.info("开始查找笔记id为 {} 的历史列表", noteId);
        List<NoteVersions> noteVersions = noteVersionsMapper.selectList(new QueryWrapper<NoteVersions>()
                .eq("note_id", noteId)
                .orderByDesc("created_at"));
        log.info("找到笔记id为 {} 的历史列表，共 {} 条", noteId, noteVersions.size());
        if (noteVersions == null){
            return new ArrayList<>();
        }
//        ThrowUtils.throwIf(noteVersions.isEmpty(), ErrorCode.SYSTEM_ERROR, "笔记历史版本不存在");
        log.info("笔记历史版本不存在");
        return noteVersions.stream()
                .map(NoteVersionsVO::toVO)
                .collect(Collectors.toList());
    }


    // ===================== 工具方法 =====================

    /**
     * 检测标题或内容是否发生变更
     */
    private boolean isTitleOrContentChanged(EditNotesRequest newNote, Notes oldNote) {
        return !ObjUtil.equal(newNote.getTitle(), oldNote.getTitle())
                || !ObjUtil.equal(newNote.getContent(), oldNote.getContent());
    }

    /**
     * 处理置顶逻辑（保证最多一个置顶笔记）
     */
    private void handlePinLogic(EditNotesRequest editRequest, Notes oldNote) {
        if (NotesIsPinne.PINNED.getKey().equals(editRequest.getIsPinned())
                && !NotesIsPinne.PINNED.getKey().equals(oldNote.getIsPinned())) {
            // 取消其他所有笔记的置顶状态
            Notes updatePinNote = new Notes();
            updatePinNote.setIsPinned(NotesIsPinne.NOT_PINNED.getKey());
            this.lambdaUpdate()
                    .eq(Notes::getIsPinned, NotesIsPinne.PINNED.getKey())
                    .update(updatePinNote);
            log.info("已取消其他笔记的置顶状态，当前笔记ID: {} 设为置顶", editRequest.getId());
        }
    }

    /**
     * 检查并清理历史版本（最多保留3个）
     */
    public void checkNoteRecord(Integer noteId) {
        QueryWrapper<NoteVersions> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("note_id", noteId);
        Long count = noteVersionsMapper.selectCount(queryWrapper);

        if (count > 3) {
            QueryWrapper<NoteVersions> deleteWrapper = new QueryWrapper<>();
            deleteWrapper.eq("note_id", noteId)
                    .orderByAsc("id") // 按ID升序，删除最旧版本
                    .last("limit 1");
            NoteVersions oldestVersion = noteVersionsMapper.selectOne(deleteWrapper);

            int deleteResult = noteVersionsMapper.deleteById(oldestVersion.getId());
            ThrowUtils.throwIf(deleteResult <= 0, ErrorCode.OPERATION_DENIED, "删除旧版本失败");
            log.info("笔记ID: {} 已删除最旧版本ID: {}", noteId, oldestVersion.getId());
        }
    }

    /**
     * 构建笔记VO（包含版本ID列表，用户信息）
     */
    public NotesVO buildNotesVo(Notes note) {
        NotesVO vo = NotesVO.toVO(note);
        List<Integer> versionIds = noteVersionsMapper.selectList(
                        new QueryWrapper<NoteVersions>().eq("note_id", note.getId()))
                .stream()
                .map(NoteVersions::getId)
                .collect(Collectors.toList());
        vo.setVersionId(versionIds);
        User user = userService.getById(note.getUserId());
        vo.setUserVO(UserVO.toVO(user));
        return vo;
    }

    /**
     * 构建笔记查询条件（含日志输出）
     * @param selectNotesRequest 前端查询参数
     * @return 构造好的QueryWrapper
     */
    public QueryWrapper<Notes> buildQueryWrapper(SelectNotesRequest selectNotesRequest) {
        log.info("开始构建笔记查询条件，请求参数：{}", JSONUtil.toJsonStr(selectNotesRequest));

        // 处理空参数（避免后续判空冗余）
        if (ObjUtil.isNull(selectNotesRequest)) {
            log.debug("查询参数为空，返回默认空条件");
            return new QueryWrapper<>();
        }

        QueryWrapper<Notes> queryWrapper = new QueryWrapper<>();

        // 1. 笔记ID（精确查询）
        Integer id = selectNotesRequest.getId();
        if (ObjUtil.isNotNull(id)) {
            queryWrapper.eq("id", id);
            log.debug("添加条件：ID精确匹配 -> id = {}", id);
        }
        // 用户id(精确查询)
        Integer userId = selectNotesRequest.getUserId();
        if (ObjUtil.isNotNull(userId)) {
            queryWrapper.eq("user_id", userId);
            log.debug("添加条件：用户ID精确匹配 -> userId = {}", userId);
        }

        // 2. 标题（模糊查询）
        String title = selectNotesRequest.getTitle();
        if (ObjUtil.isNotEmpty(title)) {
            queryWrapper.like("title", title);
            log.debug("添加条件：标题模糊匹配 -> title LIKE %{}%", title);
        }

        // 3. 内容（模糊查询）
        String content = selectNotesRequest.getContent();
        if (ObjUtil.isNotEmpty(content)) {
            queryWrapper.like("content", content);
            log.debug("添加条件：内容模糊匹配 -> content LIKE %{}%", content);
        }

        // 4. 分类（精确查询）
        String category = selectNotesRequest.getCategory();
        if (ObjUtil.isNotEmpty(category)) {
            queryWrapper.eq("category", category);
            log.debug("添加条件：分类精确匹配 -> category = {}", category);
        }

        // 5. 标签（模糊查询，假设标签存储格式为",标签1,标签2,"，避免部分匹配）
        String tags = selectNotesRequest.getTags();
        if (ObjUtil.isNotEmpty(tags)) {
            // 拼接成",标签,"格式，确保匹配完整标签（如避免"后端"匹配"后端开发"）
            String tagLike = "," + tags + ",";
            queryWrapper.like("tags", tagLike);
            log.debug("添加条件：标签模糊匹配 -> tags LIKE %{}%", tagLike);
        }

        // 6. 是否公开（精确查询）
        Integer isPublic = selectNotesRequest.getIsPublic();
        if (ObjUtil.isNotNull(isPublic)) {
            queryWrapper.eq("is_public", isPublic);
            log.debug("添加条件：是否公开 -> is_public = {}", isPublic);
        }

        // 7. 是否置顶（精确查询）
        Integer isPinned = selectNotesRequest.getIsPinned();
        if (ObjUtil.isNotNull(isPinned)) {
            queryWrapper.eq("is_pinned", isPinned);
            log.debug("添加条件：是否置顶 -> is_pinned = {}", isPinned);
        }

        // 8. 阅读量（大于等于，范围查询）
        Integer views = selectNotesRequest.getViews();
        if (ObjUtil.isNotNull(views)) {
            queryWrapper.ge("views", views); // 修正：使用ge而非eq，支持"阅读量≥X"
            log.debug("添加条件：阅读量范围 -> views ≥ {}", views);
        }

        // 9. 点赞数（大于等于，范围查询）
        Integer likes = selectNotesRequest.getLikes();
        if (ObjUtil.isNotNull(likes)) {
            queryWrapper.ge("likes", likes); // 修正：使用ge而非eq
            log.debug("添加条件：点赞数范围 -> likes ≥ {}", likes);
        }

        // 10. 删除状态（默认查询未删除）
        Integer isDeleted = selectNotesRequest.getIsDeleted();
        int deleteStatus = ObjUtil.isNull(isDeleted) ? 0 : isDeleted; // 默认为0（未删除）
        queryWrapper.eq("is_deleted", deleteStatus);
        log.debug("添加条件：删除状态 -> is_deleted = {}", deleteStatus);

        // 11. 创建时间范围（大于等于开始时间，小于等于结束时间）
        Date createTimeStart = selectNotesRequest.getCreateTimeStart();
        Date createTimeEnd = selectNotesRequest.getCreateTimeEnd();
        if (ObjUtil.isNotNull(createTimeStart)) {
            queryWrapper.ge("created_at", createTimeStart);
            log.debug("添加条件：创建时间≥ -> created_at ≥ {}", createTimeStart);
        }
        if (ObjUtil.isNotNull(createTimeEnd)) {
            queryWrapper.le("created_at", createTimeEnd);
            log.debug("添加条件：创建时间≤ -> created_at ≤ {}", createTimeEnd);
        }

        // 12. 排序逻辑（默认按创建时间降序）
        String sortField = selectNotesRequest.getSortField();
        String sortOrder = selectNotesRequest.getSortOrder();
        if (ObjUtil.isEmpty(sortField)) {
            queryWrapper.orderByDesc("created_at");
            log.debug("未指定排序字段，默认按创建时间降序排序");
        } else {
            // 支持的排序字段：views、likes、created_at、updated_at
            boolean isAsc = "asc".equalsIgnoreCase(sortOrder); // 忽略大小写
            switch (sortField) {
                case "views":
                    queryWrapper.orderBy(true,isAsc, "views");
                    log.debug("按阅读量排序：{}", isAsc ? "升序" : "降序");
                    break;
                case "likes":
                    queryWrapper.orderBy(true,isAsc, "likes");
                    log.debug("按点赞数排序：{}", isAsc ? "升序" : "降序");
                    break;
                case "createdAt":
                    queryWrapper.orderBy(true,isAsc, "created_at");
                    log.debug("按创建时间排序：{}", isAsc ? "升序" : "降序");
                    break;
                case "updatedAt":
                    queryWrapper.orderBy(true,isAsc, "updated_at");
                    log.debug("按更新时间排序：{}", isAsc ? "升序" : "降序");
                    break;
                default:
                    queryWrapper.orderByDesc("created_at");
                    log.warn("不支持的排序字段：{}，默认按创建时间降序排序", sortField);
            }
        }

        log.info("查询条件构建完成，SQL条件：{}", queryWrapper.getCustomSqlSegment());
        return queryWrapper;
    }


    /**
     * 将page<Notes>转成page<NotesVo>
     *
     * @param page
     * @return
     */
    public Page<NotesVO> toDto(Page<Notes> page) {
        List<NotesVO> voRecords = page.getRecords().stream()
                .map(this::buildNotesVo)
                .collect(Collectors.toList());

        Page<NotesVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        voPage.setRecords(voRecords);
        return voPage;
    }



}
